package info.empathymobile.forecastmethods;

public class ClassicDecompPlusHoltAndNaive implements ForecastMethod {

	public static final String NAME = "Classic Decomp plus Holt and Naïve";
	public static final int VERSION = 3;

	@Override
	public String getName() {
		return NAME;
	}
	@Override
	public int getVersion() {
		return VERSION;
	}
	
	@Override
	public int[] doForecasting(int[] timeSeries, int horizon) {
		int m = 7; //seasonal rate
		
//Trend-Cycle Component
		int[] T = new int[timeSeries.length];
		int k = (m-1)/2;
		
		for (int t = k; t < timeSeries.length-horizon-k; t++) {
			int avgSum = 0;
			for (int j = -k; j < k; j++) {
				avgSum += timeSeries[t-j];
			}
			T[t] = avgSum/m;
		}
		
//Seasonal Component
		//Detrended series yt - Tt
		int[] YminusT = new int[timeSeries.length];

		for (int t = k; t < timeSeries.length-horizon-k; t++) {
			YminusT[t] = timeSeries[t] - T[t]; //Aditive
		}
		
		//Season indexs averaged
		float [] seasonalIndexes = new float[m];
		for (int mi = 0; mi < m; mi++) {
			int sums = 0;
			for (int t = mi; t < timeSeries.length; t+=m) {
				seasonalIndexes[mi] += YminusT[t];
				sums++;
			}	
			seasonalIndexes[mi] = (int)(seasonalIndexes[mi]/sums);
		}
		
		//Normalizing to add zero
		float factor = 0;
		for (int mi = 0; mi < m; mi++) {
			factor += seasonalIndexes[mi];
		}
		factor = factor/(float)-m;
		
		for (int mi = 0; mi < m; mi++) {
			seasonalIndexes[mi] += factor;
		}
		
		//Seasonal Indexes stringed together 
		int[] S = new int[timeSeries.length];
		for (int mi = 0; mi < m; mi++) {
			for (int t = mi; t < timeSeries.length; t+=m) {
				S[t] = (int) seasonalIndexes[mi];
			}	
		}
		
//Random Component
//		int[] R = new int[timeSeries.length];
//		for (int t = k; t < timeSeries.length-horizon-k; t++) {
//			R[t] = timeSeries[t] - T[t] - S[t]; 
//		}
		
//Forecast on seasonalComponent with Naive Seasonal
		int st = S.length-horizon;
		
		for (int h = 1; h <= horizon; h++) {
			int ft = Math.abs(st+h-(((h-1)/m)+1)*m);
			
			//Y(t+h)        = Y(t+h−{[(h−1)/m]+1}m)
			S[st+h-1] = S[ft-1];
		}
		 
//Forecast on seasonallyAdjusted with Holt
		//REFERENCE: http://pratyush-chandra.blogspot.com.br/2012/05/code-for-single-double-and-triple.html
		float alpha = 0.3f;
		float beta = 0.2f;

		int[] fT = new int[T.length];
		double[] l = new double[T.length - horizon];
		double[] b = new double[T.length - horizon];
		
		//Initializations Choices
		l[0] = fT[0] = T[k];
		b[0] = T[k+1] - T[k];
		//b[0] = (T[k+3] - T[k]) / 3;
		//b[0] = (T[T.length-horizon -k - 1] - T[k])/(T.length-horizon -k - 1);

		int yt = k+1;
		fT[1] = (int) (l[0] + b[0]);
		for (; yt < T.length - horizon -k; yt++) {
			l[yt] = alpha * T[yt] + (1 - alpha) * (l[yt - 1] + b[yt - 1]);
			b[yt] = beta * (l[yt] - l[yt - 1]) + (1 - beta) * b[yt - 1];
			fT[yt+1] = (int) (l[yt] + b[yt]);
		}
			
		for (int j = 0; j < horizon + k; j++, yt++) {
			fT[yt] = (int) (l[l.length - 1] + (j + 1)
					* b[b.length - 1]);
		}
		
//SUM TO FORECAST
		for (int t = timeSeries.length-horizon; t < timeSeries.length; t++) {
			timeSeries[t] = S[t] + fT[t];
		}
		
		return timeSeries;
	}
}
